/*
  author Sylvain Bertrand <digital.ragnarok@gmail.com>
  Protected by GNU Affero GPL v3 with some exceptions.
  See README at root of alga tree.
*/
#include <linux/pci.h>
#include <linux/bitops.h>
#include <linux/cdev.h>

#include <alga/alga.h>
#include <alga/pixel_fmts.h>
#include <alga/timing.h>
#include <alga/amd/atombios/atb.h>
#include <alga/amd/dce4/dce4.h>

#include "types.h"
#include "ba.h"
#include "ucode.h"
#include "cp.h"
#include "gpu.h"
#include "irq.h"
#include "drv.h"
#include "ih.h"
#include "dce.h"

#include "regs.h"

int intrs_enable(struct pci_dev *dev)
{
	struct dev_drv_data *drv_data;
	u32 cp_int_ctl;
	u32 grbm_int_ctl;

	drv_data = pci_get_drvdata(dev);

	cp_int_ctl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE
					| RB_INT_ENABLE | TIME_STAMP_INT_ENABLE;

	grbm_int_ctl = 0;	
	grbm_int_ctl |= GUI_IDLE_INT_ENABLE;

	wr32(dev, cp_int_ctl, CP_INT_CTL);
	wr32(dev, grbm_int_ctl, GRBM_INT_CTL);

	return dce_intrs_enable(dev);
}

void intrs_reset(struct pci_dev *dev)
{
	struct dev_drv_data *drv_data;
	drv_data = pci_get_drvdata(dev);

	/* works even if ucode is not loaded */
	wr32(dev, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE, CP_INT_CTL);

	wr32(dev, 0, GRBM_INT_CTL);

	dce_intrs_reset(dev);
}

irqreturn_t irq_thd(int irq, void *dev_id)
{
	struct pci_dev *dev;
	struct dev_drv_data *drv_data;
	unsigned i;

	dev = dev_id;
	drv_data = pci_get_drvdata(dev);

	/* TODO: wrong, should use the ih ring buffer, but it's ok for DCE intrs... for now */
	for (i = 0; i < drv_data->crtcs_n; ++i)
		if (drv_data->irq.hpd & BIT(i)) {
			int r;
			r =dce4_hpd_intr(drv_data->dce, i);
			if (r != 0)
				dev_err(&dev->dev, "unable to service DCE "
							"interrupt requests\n");
		}

	ih_enable(dev);
	return IRQ_HANDLED;
}

/* XXX: not sharing data with irq thread, then mutex is ok to lock the dce */
irqreturn_t irq(int irq, void *dev_id)
{
	struct pci_dev *dev;
	dev = dev_id;

	dce_irqs_ack(dev);
	ih_disable(dev);
	return IRQ_WAKE_THREAD;
}
